#include "Calendar.h"
#include <iostream>

using namespace std;

Calendar::Calendar() {
    entryNo = 0;
}

Calendar::Calendar(long entryNo, const priority_queue<Event, vector<Event>, Event::EventComparator>& events) {
    this->entryNo = entryNo;
    this->events = events;
}

Calendar::Calendar(const Calendar& copied) {
    this->entryNo = copied.entryNo;
    this->events = copied.events;
}

Calendar& Calendar::operator=(const Calendar& copied) {
    this->entryNo = copied.entryNo;
    this->events = copied.events;
    return *this;
}

Calendar::~Calendar() {
    this->entryNo = 0;
    while (!this->events.empty()) {
        this->events.pop();
    }
}

vector<string> SplitString(string buff, char splitChar) {
    stringstream stream(buff);
    string word;
    vector<string> splitresult;
    while (getline(stream, word, splitChar)) {
        splitresult.push_back(word);
    }
    return splitresult;
}

void Calendar::ViewEvent(char** args) {
    if (args[2]) {
        long entryNo = atol(args[2]);
        int i;
        for (i = 0; i < events.size(); i++) {
            if ((*(&(events.top()) + i)).GetEntryNo() == entryNo) {
                cout << (*(&(events.top()) + i)).GetEntryNo() << endl;
                break;
            }
        }
    }
}

void Calendar::AddEvent(char** args) {
    bool found = false;
    if (args[2]) {
        long entryNo = atol(args[2]);
        int i;
        for (i = 0; i < events.size(); i++) {
            if ((*(&(events.top()) + i)).GetEntryNo() == entryNo) {
                found = true;
                break;
            }
        }
        if (found) {
            printf("Event with entry number %ld already exists \n", entryNo);
        } else {
            printf("Please fill in the event details \n");
            // read event description
            printf("Event description : \n\t");
            string description;
            cin >> description;

            // read event position
            printf("Event position (lat, long) : \n\t");
            string buff;
            cin >> buff;
            vector<string> splitresult = SplitString(buff, ',');
            Point<float> position(atof(splitresult[0].c_str()), atof(splitresult[1].c_str()));

            // read date time
            printf("Event date and time (YYYY-MM-DD@hour:min:sec) : \n\t");
            string date, time_string;
            cin >> buff;
            splitresult.clear();
            splitresult = SplitString(buff, '@');
            date = splitresult[0];
            time_string = splitresult[1];
            // split date
            struct tm timeinfo;

            splitresult.clear();
            splitresult = SplitString(date, '-');
            timeinfo.tm_year = atoi(splitresult[0].c_str()) - 1900;
            timeinfo.tm_mon = atoi(splitresult[1].c_str()) - 1;
            timeinfo.tm_mday = atoi(splitresult[2].c_str());

            splitresult.clear();
            splitresult = SplitString(time_string, ':');
            timeinfo.tm_hour = atoi(splitresult[0].c_str());
            timeinfo.tm_min = atoi(splitresult[1].c_str());
            timeinfo.tm_sec = atoi(splitresult[2].c_str());

            // read participants
            printf("Number of participants : \n\t");
            int participantCount, iter;
            vector<string> participants;
            scanf("%d", &participantCount);
            cin >> buff;
            for (iter = 0; iter < participantCount; iter++) {
                printf("        Participant #%d : ", iter + 1);
                cin >> buff;
                participants.push_back(buff);
            }
            // insert event to event list
            Event _event;
            _event.SetEntryNo(entryNo);
            _event.SetDateTime(&timeinfo);
            _event.SetPosition(position);
            _event.SetDescription(description);
            _event.SetParticipants(participants);
            _event.SetParticipantCount(participantCount);
            events.push(_event);

            printf("Success inserting new event with entry number %ld \n", entryNo);
        }
    }
}

void Calendar::DeleteEvent(char** args) {
    bool found = false;
    if (args[2]) {
        long entryNo = atol(args[2]);
        int i;
        for (i = 0; i < events.size(); i++) {
            if ((*(&(events.top()) + i)).GetEntryNo() == entryNo) {
                found = true;
                break;
            }
        }
        if (!found) {
            printf("Event with entry number %ld not found  \n", entryNo);
        } else {
            priority_queue<Event, vector<Event>, Event::EventComparator> tempEvents;
            // event exists, delete event
            for (i = 0; i < events.size(); i++) {
                Event tempEvent = events.top();
                events.pop();
                if (tempEvent.GetEntryNo() != entryNo) {
                    // add to temp events
                    tempEvents.push(tempEvent);
                }
            }
            events = tempEvents;
            printf("Success deleting new event with entry number %ld \n", entryNo);
        }
    }
}

void Calendar::LoadCalendar(char** args) {
    if (args[2]) {
        File f(args[2]);
        string file_content = f.readAllFile();
        //CSString currEvent [100];
        vector<string> currEvent;
        //CSString eventData[100];
        vector<string> eventData;
        //CSString pointData[100];
        vector<string> pointData;
        //Split(string, currEvent, "#");
        stringstream stream(file_content);
        string word;
        while (getline(stream, word, '#')) {
            currEvent.push_back(word);
        }
        stream.clear();
        //if (currEvent[0]) {
        if (currEvent.size() > 0) {
            int eventCount;
            //eventCount = atoi(Trim(currEvent[0]));
            eventCount = atoi(currEvent[0].c_str());
            for (int i = 0; i < eventCount; ++i) {
                //CSString eventDataTrimmed = Trim(currEvent[i + 1]);
                //int dataLength = Split(eventDataTrimmed, eventData, "\n");                
                eventData.clear();
                pointData.clear();

                stream.str(currEvent[i + 1]);
                while (getline(stream, word, '\n')) {
                    eventData.push_back(word);
                }
                stream.clear();
                Event _event;
                if (eventData.size() >= 4) {
                    //set number
                    //SetEntryNo(&_event, atol(Trim(eventData[0])));
                    _event.SetEntryNo(atol(eventData[0].c_str()));
                    //set description
                    //SetDescription(&_event, Trim(eventData[1]));
                    //cout<<"Description: "<<eventData[1]<<endl;                  
                    _event.SetDescription(eventData[1]);
                    //set position
                    //int pointLength = Split(Trim(eventData[2]), pointData, ",");
                    stream.str(eventData[2]);
                    while (getline(stream, word, ',')) {
                        pointData.push_back(word);
                    }
                    stream.clear();
                    if (pointData.size() >= 2) {
                        //CSString latitude = Trim(pointData[0]);
                        float latitude = atof(pointData[0].c_str());
                        //CSString longitude = Trim(pointData[1]);
                        float longitude = atof(pointData[1].c_str());
                        //Point position = MakePoint(atof(latitude), atof(longitude));
                        Point<float> point(latitude, longitude);
                        //SetPosition(&_event, position);
                        _event.SetPosition(point);
                    } else {
                        //printf("Point has Wrong Format \n");-->exception
                        throw WrongFormatException("Point has Wrong Format");
                    }
                    //set date time
                    //SetDateTime(&_event, ParseDateTime(Trim(eventData[3])));
                    vector<string> temp_data;
                    stream.str(eventData[3]);
                    while (getline(stream, word, '@')) {
                        temp_data.push_back(word);
                    }
                    stream.clear();
                    if (temp_data.size() >= 2) {
                        string date_string = temp_data[0];
                        stream.str(date_string);
                        vector<string> date;
                        while (getline(stream, word, '-')) {
                            date.push_back(word);
                        }
                        stream.clear();
                        string time_string = temp_data[1];
                        stream.str(time_string);
                        vector<string> time_v;
                        while (getline(stream, word, ':')) {
                            time_v.push_back(word);
                        }
                        stream.clear();
                        string hari, bulan, tahun, jam, menit, detik;
                        if (date.size() >= 3) {
                            tahun = date[0];
                            bulan = date[1];
                            hari = date[2];
                        } else {
                            //--> exception
                            throw WrongFormatException("DateTime has Wrong Format");
                        }
                        if (time_v.size() >= 3) {
                            jam = time_v[0];
                            menit = time_v[1];
                            detik = time_v[2];
                        } else {
                            //--> exception
                            throw WrongFormatException("DateTime has Wrong Format");
                        }

                        time_t rawtime;
                        struct tm* timeinfo = (tm*) malloc(sizeof (tm));
                        //time(&rawtime);
                        //timeinfo = localtime(&rawtime);
                        timeinfo->tm_year = atoi(tahun.c_str()) - 1900;
                        timeinfo->tm_mon = atoi(bulan.c_str()) - 1;
                        timeinfo->tm_mday = atoi(hari.c_str());
                        timeinfo->tm_hour = atoi(jam.c_str());
                        timeinfo->tm_min = atoi(menit.c_str());
                        timeinfo->tm_sec = atoi(detik.c_str());
                        _event.SetDateTime(timeinfo);
                        free(timeinfo);

                    } else {
                        //--> exception
                        throw WrongFormatException("DateTime has Wrong Format");
                    }


                    //set participants
                    vector<string> participants;
                    //int participantCount = Split(Trim(eventData[4]), participants, ",");
                    string participants_string = eventData[4];
                    stream.str(participants_string);
                    while (getline(stream, word, ',')) {
                        participants.push_back(word);
                    }
                    stream.clear();
                    //int participantCounter;
                    //for (participantCounter = 0; participantCounter < participantCount; participantCounter++) {
                    //   participants[participantCounter] = Trim(participants[participantCounter]);
                    //}
                    //SetParticipants(&_event, participants, participantCount);
                    _event.SetParticipantCount(participants.size());
                    _event.SetParticipants(participants);
                    //InsertEvent(&(calendar->events), _event);
                    cout << "mask" << endl;
                    this->events.push(_event);
                    //DeallocParticipants(&_event);
                } else {
                    //printf("Wrong File Format Parameter Count\n");--> exception
                    throw WrongFormatException("Wrong Event Format Parameter Count");
                }
            }
        } else {
            //printf("Wrong File Format : No Event Number\n");--> exception
            throw WrongFormatException("Wrong File Format : No Event Number");
        }
    } else {
        printf("Please specify file name\n");
        printf("Usage Load <filename>");
    }
}

void Calendar::SaveCalendar(char** args) {
    //##

    if (args[1]) {        
        File f(args[1]);
        if (this->events.size() == 0) {        
            f.writeToFile("0\n");
        } else {     
            
            int eventCount = this->events.size();            
            ostringstream oss;
            oss << eventCount;
            f.writeToFile(oss.str() + "\n");            
            for (int i = 0; i < events.size(); i++) {
                Event event = *(&(events.top()) + i);
                f.writeAppendToFile(event.toString());
            }
        }
    }

}

void Calendar::PrintInfo(char**argv) {
    int i;
    //    cout<<"isi: "<<events.size()<<endl;
    for (i = 0; i < events.size(); i++) {
        Event event = *(&(events.top()) + i);
        cout << event.toString() << endl;
    }
}

bool Calendar::EmptyEvent() {
    return (events.size() == 0);
}

vector<long> Calendar::GetEventCountInDay(struct tm* day) {
    struct tm* time;
    int retVal = 0;
    int i;
    vector<long> entryNoBuff;
    for (i = 0; i < events.size(); i++) {
        Event currEvent = *(&(events.top()) + i);
        time = currEvent.GetDateTime();
        if ((day->tm_year == time->tm_year) && (day->tm_mon == time->tm_mon) && (day->tm_mday == time->tm_mday)) {
            entryNoBuff.push_back(currEvent.GetEntryNo());
            retVal++;
        }
    }
    return entryNoBuff;
}

void Calendar::PrintFirst(char** args) {
    int count = atoi(args[2]);
    int i;

    if (count >= events.size()) {
        PrintInfo(args);
    } else {
        for (i = 0; i < count; i++) {
            Event currEvent = *(&(events.top()) + i);
            cout << currEvent.toString() << endl;
        }
    }
}

void Calendar::PrintLast(char** args) {
    int count = atoi(args[2]);
    int i;

    if (count >= events.size()) {
        PrintInfo(args);
    } else {
        for (i = events.size() - count; i < events.size(); i++) {
            Event currEvent = *(&(events.top()) + i);
            cout << currEvent.toString() << endl;
        }
    }
}

void Calendar::ShowDetail(char**args) {
    if (args[1]) {
        string date = args[2];
        int iter;
        vector<string> splitresult = SplitString(date, '-');
        struct tm* dateInfo;

        dateInfo->tm_year = atoi(splitresult[0].c_str()) - 1900;
        dateInfo->tm_mon = atoi(splitresult[1].c_str()) - 1;
        dateInfo->tm_mday = atoi(splitresult[2].c_str());

        vector<long> entryNoBuff = GetEventCountInDay(dateInfo);

        if (entryNoBuff.size() > 0) {
            printf("Found %d event(s) : \n", entryNoBuff.size());
            for (iter = 0; iter < entryNoBuff.size(); iter++) {
                printf("    Event no #%ld \n", entryNoBuff[iter]);
            }
        } else {
            printf("Can't find event\n");
        }
    }
}

void Calendar::SetEvents(priority_queue<Event, vector<Event>, Event::EventComparator> newEvents) {
    events = newEvents;
}

priority_queue<Event, vector<Event>, Event::EventComparator> Calendar::GetEvents() {
    return events;
}

void Calendar::SetEntryNo(long newEntryNo) {
    entryNo = newEntryNo;
}

long Calendar::GetEntryNo() {
    return entryNo;
}

bool IsYearKabisat(int year) {
    if (year % 4 == 0) {
        if (year % 100 == 0) {
            if (year % 400 == 0) {
                return true;
            } else {
                return false;
            }
        } else {
            return true;
        }
    } else {
        return false;
    }
}

int Calendar::MaxDaysInMonth(int month, int year) {
    switch (month) {
        case 1:
        {
            return 31;
        }
        case 2:
        {
            if (IsYearKabisat(year)) {
                return 29;
            } else {
                return 28;
            }
        }
        case 3:
        {
            return 31;
        };
        case 4:
        {
            return 30;
        };
        case 5:
        {
            return 31;
        };
        case 6:
        {
            return 30;
        };
        case 7:
        {
            return 31;
        };
        case 8:
        {
            return 31;
        };
        case 9:
        {
            return 30;
        };
        case 10:
        {
            return 31;
        };
        case 11:
        {
            return 30;
        };
        case 12:
        {
            return 31;
        };
        default:
        {
            return 0;
        }
    }
}

int Calendar::GetFirstDay(int year, int month) {
    tm a = {0, 0, 0, 1, month - 1, year - 1900};
    tm b = {0, 0, 0, 1, 0, 90};
    time_t x = mktime(&a);
    time_t y = mktime(&b);
    int dayDiff = difftime(x, y) / (60 * 60 * 24);
    return (dayDiff % 7) + 1;
}


